package org.ecpkn.PAGridSim.core.gridsimEntity.policy.paPolicy.preemptable;

import gridsim.GridSim;
import gridsim.Gridlet;
import gridsim.PE;
import gridsim.ResGridlet;
import gridsim.ResGridletList;

import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;

import org.ecpkn.PAGridSim.core.gridsimEntity.policy.paPolicy.PAPolicy;

public abstract class PreemptablePAPolicy extends PAPolicy implements
		Preemptable {

	/**
	 * Allocates a new PreemptablePartitionPAPolicy object
	 * 
	 * @param resourceName
	 *            GridResource name that will contain this allocation policy
	 * @param entityName
	 *            this object entity name
	 * @throws Exception
	 *             This happens when one of the following scenarios occur:
	 *             creating this entity before initializing GridSim package;
	 *             this entity name is null or empty;this entity has zero number
	 *             of PEs (Processing Elements). No PEs mean the Gridlets can't
	 *             be processed. A GridResource must contain one or more
	 *             Machines. A Machine must contain one or more PEs.
	 * @see gridsim.GridSim#init(int, Calendar, boolean, String[], String[],
	 *      String)
	 * @pre resourceName != null;entityName != null
	 */
	public PreemptablePAPolicy(String resourceName, String policyName)
			throws Exception {
		super(resourceName, policyName);
	}

	public void preemptionProcesse(ResGridlet rglToBePreemted,
			ResGridlet rglPreempt) {
		// updates all the Gridlets first before pausing
		updateGridletProcessing();
		// Removes the Gridlet from the execution list
		gridletInExecList_.remove(rglToBePreemted);
		// if a Gridlet is finished upon canceling, then set it to success
		if (rglToBePreemted.getRemainingGridletLength() == 0.0) {
			gridletFinish(rglToBePreemted, Gridlet.SUCCESS);
		} else {
			recordExecutionTrace(rglToBePreemted, GridSim.clock());
			rglToBePreemted.setGridletStatus(Gridlet.PAUSED); // change the
																// status
			gridletPausedList_.add(rglToBePreemted); // add into the paused list
		}
		super.resource_.setStatusPE(PE.FREE, rglToBePreemted.getMachineID(),
				rglToBePreemted.getPEID());
		// empty slot is available, hence process a new Gridlet
		allocatePEtoGridlet(rglPreempt, rglToBePreemted.getMachineID(),
				rglToBePreemted.getMachineID());
	}

	public ResGridlet preemptionDecision(ResGridlet rgl,
			Comparator<ResGridlet> comparator, ResGridletList rgList) {
		ResGridletList compareList = new ResGridletList();
		compareList.addAll(rgList);
		compareList.add(rgl);
		Collections.sort(compareList, comparator);
		return compareList.getLast();
	}

	/**
	 * A come-in Rgl try to preempt a place among the rglListToBeChecked
	 * 
	 * @param inComingRgl
	 * @param rglListToBeChecked
	 */
	protected void tryToPreempt(ResGridlet inComingRgl,
			ResGridletList rglListToBeChecked) {
		ResGridlet rglToBePreempted = preemptionDecision(inComingRgl,
				getPriorityComparator(), rglListToBeChecked);
		if (inComingRgl.equals(rglToBePreempted)) {
			inComingRgl.setGridletStatus(Gridlet.QUEUED);
			gridletQueueList_.add(inComingRgl);
			updateAllGridletList();
		} else {
			preemptionProcesse(rglToBePreempted, inComingRgl);
		}
	}

	/**
	 * update gridletQueueList,inExecList,and pausedList
	 */
	private void updateAllGridletList() {
		Collections.sort(gridletQueueList_, getPriorityComparator());
		Collections.sort(gridletInExecList_, getPriorityComparator());
		Collections.sort(gridletPausedList_, getPriorityComparator());
	}
}
